package com.example.camerafacedetect.faceframe;

import android.graphics.Bitmap;
import android.graphics.ImageFormat;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.YuvImage;
import android.media.Image;
import android.util.Log;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.nio.ByteBuffer;

/**
 * Created by yubo on 2015/8/31.
 * 图像处理的工具类
 */
public class ImageUtils {

    /** 将yuv数据转换为jpeg */
    public static byte[] yuv2Jpeg(byte[] yuvBytes, int width, int height) {
        YuvImage yuvImage = new YuvImage(yuvBytes, ImageFormat.NV21, width, height, null);

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        yuvImage.compressToJpeg(new Rect(0, 0, width, height), 100, baos);

        return baos.toByteArray();
    }

    /** 旋转图像 */
    public static Bitmap rotateBitmap(Bitmap sourceBitmap, int degree) {
        Matrix matrix = new Matrix();
        //旋转90度，并做镜面翻转
        matrix.setRotate(degree);
        matrix.postScale(-1, 1);
        return Bitmap.createBitmap(sourceBitmap, 0, 0, sourceBitmap.getWidth(), sourceBitmap.getHeight(), matrix, true);
    }

    /** 保存bitmap到文件 */
    public static void saveBitmap(Bitmap bitmap, String path) {
        if(bitmap != null) {
            try {
                bitmap.compress(Bitmap.CompressFormat.JPEG, 100, new FileOutputStream(new File(path)));
            } catch (FileNotFoundException e) {
                e.printStackTrace();
            }
        }
    }
    private static final int COLOR_FormatI420 = 1;
    private static final int COLOR_FormatNV21 = 2;
    public static byte[] getDataFromImage(Image image, int colorFormat) {
        if (colorFormat != COLOR_FormatI420 && colorFormat != COLOR_FormatNV21) {
            throw new IllegalArgumentException("only support COLOR_FormatI420 " + "and COLOR_FormatNV21");
        }
        // 过去Frame里面的图像区域
        Rect crop = image.getCropRect();
        // 获取图片格式，每个数字代表一种格式
        int format = image.getFormat();
        // 获取crop的宽高
        int width = crop.width();
        int height = crop.height();
        // 获取颜色通道，这里是YUV三个通道
        Image.Plane[] planes = image.getPlanes();
        // 颜色深度，每个像素位数
        int totaolLength = ImageFormat.getBitsPerPixel(format);//12
        // 一帧的数据
        byte[] data = new byte[width * height * ImageFormat.getBitsPerPixel(format) / 8];//yuv一帧数据
        // 构建一行数据的容器(planes[0].getRowStride()=640)
        byte[] rowData = new byte[planes[0].getRowStride()];//一行数据
        int channelOffset = 0;
        int outputStride = 1; // 输出间隔，=1代表没有间隔，逐个填充
        // 遍历planes
        for (int i = 0; i < planes.length; i++) {
            // 设置每种场景下的channeloffset以及outputstride
            switch (i) {
                case 0:
                    channelOffset = 0;
                    outputStride = 1;
                    break;
                case 1:
                    if (colorFormat == COLOR_FormatI420) {
                        channelOffset = width * height;
                        outputStride = 1;
                    } else if (colorFormat == COLOR_FormatNV21) {
                        channelOffset = width * height + 1;
                        outputStride = 2;
                    }
                    break;
                case 2:
                    if (colorFormat == COLOR_FormatI420) {
                        channelOffset = (int) (width * height * 1.25);
                        outputStride = 1;
                    } else if (colorFormat == COLOR_FormatNV21) {
                        channelOffset = width * height;
                        outputStride = 2;
                    }
                    break;
            }
            // 获取指定通道的数据（存储在buffer结构中）
            ByteBuffer buffer = planes[i].getBuffer();
            // rowstride就是一行像素数
            int rowStride = planes[i].getRowStride();// i=0,rowstride=640
            // 设定采样间隔，Y通道是全采样，即无损采样，值是1，UV通道，在422模式下，采样数是Y通道的一半，所以间隔为2
            int pixelStride = planes[i].getPixelStride();//Y-->1 U/V-->2 i=0, pixelStride=1

            // 第一个plane不需要位移
            // 其他palne需要右移1位
            int shift = (i == 0) ? 0 : 1;
            // 右移相当于除法，位移为1相当于除2，即减小分辨率
            // Y通道全采样，w和h等于原图大小；UV通道下采样1/2，则w和h都只保留一半（除以2）即可
            int w = width >> shift; // w = 640/2 = 320
            int h = height >> shift; // h = 480/2 = 240
            // top=0，left=0，所以position=0，这里计算buffer（数组）的下标位置，用来后面根据postion来获取里面的数据
            // 因为是要指定第一个数据，所以position必然为0，不过后面所有的position计算都是采用此公式
            int position = rowStride * (crop.top >> shift) + pixelStride * (crop.left >> shift);//0
            buffer.position(position);//buff位置从0开始
            //一行一行添加像素
            for (int row = 0; row < h; row++) {
                int length;
                // 如果是Y通道数据
                if (pixelStride == 1 && outputStride == 1) {
                    length = w;//一行数据的长度
                    // 从buffer中的channeloffset位置，获取length长度的数据放到data数组中
                    buffer.get(data, channelOffset, length);
                    channelOffset += length; // offset位置要向后移动
                }
                // 如果是UV通道
                else {
                    // 这样计算可以保证只需要取length个数据就可以采样到w个数据
                    // 注意，并不是要获取完整一行数据，再间隔的去采样，真是处理流程是取出少于一行的数据即可满足数量
                    length = (w - 1) * pixelStride + 1;//1439 偶数位都是像素 所以只要到长度的最后一个字节即可
                    buffer.get(rowData, 0, length);//保存这一行数据 取了lenght长后 下次取数据的时候将从lenht位置开始
                    // 采样w次（采用间隔采样）
                    for (int col = 0; col < w; col++) {
                        // 作为UV通道，需要隔一个像素采样一次，即下表为偶数的数据进行采样；
                        // col * pixelStride的值：2，4，6，8
                        data[channelOffset] = rowData[col * pixelStride];//也就是说每隔一个字节才是像素  也就是偶数位置的都是像素
                        channelOffset += outputStride;//下一个像素
                    }
                }
                // 只要不是最后一行，就需要设定一下buffer的位置，即下一次个取值buffer的起始位置
                if (row < h - 1) {
                    //下一次取出数据从那个位置开始
                    // buffer当前行的起始位置+一行长度=>下一行的其实位置，然后再进行倒推
                    buffer.position(buffer.position() + rowStride - length);
                }
            }
        }

        return data;
    }

}
